home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_400 / 424_01 / ed_157 / find_files.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-12-08  |  14.9 KB  |  640 lines

  1. /*
  2.  * Copyright (C) 1992 by Rush Record
  3.  * Copyright (C) 1993 by Charles Sandmann (sandmann@clio.rice.edu)
  4.  * 
  5.  * This file is part of ED.
  6.  * 
  7.  * ED is free software; you can redistribute it and/or modify it under the terms
  8.  * of the GNU General Public License as published by the Free Software Foundation.
  9.  * 
  10.  * ED is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  11.  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12.  * PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  13.  * 
  14.  * You should have received a copy of the GNU General Public License along with ED
  15.  * (see the file COPYING).  If not, write to the Free Software Foundation, 675
  16.  * Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18. #include "opsys.h"
  19.  
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <limits.h>
  24. #include "file.h"
  25. #ifdef NeXT
  26. #include <sys/dir.h>
  27. #else
  28. #ifndef VMS
  29. #ifdef DIRENT_PC
  30. #include "direntpc.h"
  31. #else
  32. #include <dirent.h>
  33. #endif
  34. #endif
  35. #endif
  36.  
  37. #include "memory.h"
  38. #include "ctyp_dec.h"
  39.  
  40. #define CHARLES 0x79861876    /* magic number */
  41.  
  42. #ifdef VMS
  43. typedef struct chardesc {unsigned short length,class;Char *string;} desc_node;
  44. typedef struct chardesc *desc;
  45. static Char wildcard[512],lastmatch[512];
  46. #else
  47. typedef struct namelist_str *namelist_ptr;
  48. typedef struct namelist_str
  49. {
  50.     namelist_ptr next;
  51.     Char *name;
  52. } namelist_node;
  53.  
  54. typedef struct ff_str *ff_ptr;
  55. typedef struct ff_str
  56. {
  57.     Int magic,matchcount;
  58.     namelist_ptr firstname,curname;    /* these are loaded during scan. when user traverses, curname handles it. */
  59. } ff_node;
  60. #endif
  61.  
  62. /******************************************************************************\
  63. |Routine: match_wild
  64. |Callby: find_files
  65. |Purpose: Provides VMS-style matching of file names to a template. Returns 1 on a match, else 0.
  66. |Arguments:
  67. |    template is the template string, which may contain * and % wildcards.
  68. |    string is the string we are comparing to the template.
  69. \******************************************************************************/
  70. Int match_wild(template,string)
  71. Char *template,*string;
  72. {
  73.     register Int remain_t,remain_s;
  74.     register Char *t,*s,c;
  75.     Int save_r_s,save_r_t;
  76.     Char *save_t,*save_s;
  77. #ifdef GNUDOS
  78.     register Char sc;
  79. #endif
  80.  
  81.     t = template;
  82.     s = string;
  83.     remain_t = strlen(t);
  84.     remain_s = strlen(s);
  85.     save_r_s = 0;
  86.     while(1)
  87.     {
  88.         if(--remain_t < 0)
  89.         {
  90.             if(!remain_s)
  91.                 return(1);
  92.         }
  93.         else
  94.         {
  95.             if((c = *t++) == '*')
  96.             {
  97.                 if(*t == '*')
  98.                 {
  99.                     t++;
  100.                     remain_t--;
  101.                 }
  102.                 else
  103.                 {
  104.                     if(!remain_t)
  105.                         return(1);
  106.                     save_s = s;
  107.                     save_t = t;
  108.                     save_r_s = remain_s;
  109.                     save_r_t = remain_t;
  110.                     continue;
  111.                 }
  112.             }
  113.             if(--remain_s < 0)
  114.                 return(0);
  115. #ifdef GNUDOS
  116.             sc = *s++;
  117.             if(tolower(c) == tolower(sc))
  118. #else
  119.             if(c == *s++)
  120. #endif
  121.                 continue;
  122.             if(c == '?')
  123.                 continue;
  124.         }
  125.         if(--save_r_s < 0)
  126.             return(0);
  127.         save_s++;
  128.         s = save_s;
  129.         t = save_t;
  130.         remain_s = save_r_s;
  131.         remain_t = save_r_t;
  132.     }
  133. }
  134.  
  135. #ifndef VMS
  136.  
  137. /******************************************************************************\
  138. |Routine: got_string
  139. |Callby: find_files
  140. |Purpose: Makes a buffer and copies a string into it.
  141. |Arguments:
  142. |    string is the string to be copied.
  143. \******************************************************************************/
  144. Char *got_string(string)
  145. Char *string;
  146. {
  147.     Char *p;
  148.     
  149.     p = (Char *)imalloc(((string)? strlen(string) : 0) + 1);
  150.     if(string)
  151.         strcpy(p,string);
  152.     else
  153.         *p = '\0';
  154.     return(p);
  155. }
  156.  
  157. /******************************************************************************\
  158. |Routine: ff_report_name
  159. |Callby: find_files
  160. |Purpose: Stores a validated, matching file name in dynamic memory.
  161. |Arguments:
  162. |    ff is the header of the (possibly empty) list of already-validated names.
  163. |    name is the new name to append to the list.
  164. \******************************************************************************/
  165. void ff_report_name(ff,name)
  166. ff_ptr ff;
  167. Char *name;
  168. {
  169.     namelist_ptr p;
  170.     Char *from,*to,c;
  171.     
  172.     if(!ff->firstname)
  173.         ff->firstname = ff->curname = (namelist_ptr)&ff->firstname;
  174.     p = ff->curname->next = (namelist_ptr)imalloc(sizeof(namelist_node));
  175.     ff->curname = p;
  176.     p->next = NULL;
  177.     p->name = got_string(name);
  178.     for(from = to = p->name;(c = *from++);)    /* eliminate double /s */
  179.         if(c == '/')
  180.         {
  181.             if(*from != '/')
  182.             {
  183.                 if(*from == '.' && *(from + 1) == '/')
  184.                 {
  185.                     *to++ = '/';
  186.                     from += 2;
  187.                 }
  188.                 else
  189.                     *to++ = c;
  190.             }
  191.         }
  192.         else
  193.             *to++ = c;
  194.     *to = '\0';
  195.     ff->matchcount++;
  196. }
  197.  
  198. /******************************************************************************\
  199. |Routine: ff_scan_file
  200. |Callby: find_files
  201. |Purpose: Generates a complete list of matching file names (in inode order).
  202. |Arguments:
  203. |    ff is the list header structure.
  204. |    wild is a file specification possibly containing * or ? or ... wildcards.
  205. \******************************************************************************/
  206. void ff_scan_file(ff,wild)
  207. ff_ptr ff;
  208. Char *wild;
  209. {
  210.     Char *wil,wildcard;
  211.     Char *pred,*foll,*full,*temp,*etc,*dirmatch,*temp2;
  212.     Int lpred,lfoll,lname,letc;
  213.     Char *p,*q;
  214.     DIR *dp;    /* directory pointer */
  215.     struct stat statbuf;
  216. #ifdef NeXT
  217.     struct direct *d;
  218. #else
  219.     struct dirent *d;
  220. #endif
  221.     
  222. /* Algorithm:
  223.  *
  224.  *    if no wildcards:
  225.  *        return(!stat(file))
  226.  *    else if wild = /.../:
  227.  *        find preceeding /: pred/.../foll
  228.  *        if pred is null, pred = / (or /. if GNUDOS)
  229.  *        opendir(pred), scan=pfile:
  230.  *            if pfile is a directory:
  231.  *                recurse with /pred/pfile/.../foll
  232.  *                if foll contains /: folldir/folletc
  233.  *                    if pfile matches folldir
  234.  *                        recurse with pred/pfile/folletc
  235.  *            else
  236.  *                if foll does not contain /
  237.  *                    if pfile matches foll
  238.  *                        return a hit
  239.  *    else
  240.  *        find preceeding /: pred/foll
  241.  *        if foll contains /:
  242.  *            foll divides into dirmatch/etc
  243.  *            if pred is null, pred = / (or /. if GNUDOS)
  244.  *            opendir(pred), scan=pfile
  245.  *                if pfile is a directory:
  246.  *                    if pfile matches dirmatch
  247.  *                        recurse with pred/pfile/foll
  248.  *        else
  249.  *            if pred is null, pred = / (or /. if GNUDOS)
  250.  *            opendir(pred), scan=pfile
  251.  *                if pfile matches foll
  252.  *                    return a hit
  253.  */        
  254.     wil = got_string(wild);    /* make a modifiable copy of the string */
  255.     for(p = wil;*p;p++)
  256.         if(*p == '*' || *p == '?' || !strncmp(p,"/.../",5))
  257.             break;
  258.     if(!*p)
  259.     {
  260.         if(!stat(wild,&statbuf))
  261.             ff_report_name(ff,wild);
  262.         ifree(wil);
  263.         return;
  264.     }
  265.     if(*p == '/')    /* save the type of wildcard */
  266.         p++;    /* advance to first . if it's a /.../ */
  267.     wildcard = *p;
  268.     while(--p != wil)    /* find the / preceeding the wildcard */
  269.         if(*p == '/')
  270.             break;
  271.     *p++ = '\0';
  272.     pred = got_string((*wil)? wil : (Char *)"/");    /* null predecessor means open root directory for scan */
  273.     if(wildcard == '.')
  274.     {
  275.         while(*p++ != '/');    /* find the other end of the /.../ */
  276.         foll = got_string((*p)? p : (Char *)"*");
  277.         if((dp = opendir(pred)))
  278.         {
  279.             lpred = strlen(pred);
  280.             lfoll = strlen(foll);
  281.             while((d = readdir(dp)))    /* read entries */
  282.             {
  283. #ifndef GNUDOS
  284.                 if(!d->d_ino)    /* skip empty entries */
  285.                     continue;
  286. #endif
  287.                 if(!strcmp(d->d_name,".") || !strcmp(d->d_name,".."))    /* ignore . and .. */
  288.                     continue;
  289. /* build pred/name */
  290.                 full = (Char *)imalloc(lpred + 1 + (lname = strlen(d->d_name)) + 1);
  291.                 memcpy(full,pred,lpred);
  292.                 p = full + lpred;
  293.                 *p++ = '/';
  294.                 strcpy(p,d->d_name);
  295.                 if(!stat(full,&statbuf))    /* see if the file found is a directory */
  296.                 {
  297.                     if(S_ISDIR(statbuf.st_mode))
  298.                     {
  299.                         temp = (Char *)imalloc(lpred + 1 + lname + 5 + lfoll + 1);
  300.                         strcpy(temp,full);
  301.                         strcat(temp,"/.../");
  302.                         strcat(temp,foll);
  303.                         ff_scan_file(ff,temp);
  304.                         ifree(temp);
  305.                         if((p = (Char *)strchr(foll,'/')))
  306.                         {
  307.                             temp2 = got_string(foll);
  308.                             temp2[p - foll] = '\0';
  309.                             if(match_wild(temp2,d->d_name))
  310.                             {
  311.                                 etc = got_string((*p)? p : (Char *)"*");
  312.                                 temp = (Char *)imalloc(lpred + 1 + lname + 1 + strlen(etc) + 1);
  313.                                 strcpy((q = temp),pred);
  314.                                 q += lpred;
  315.                                 *q++ = '/';
  316.                                 strcpy(q,d->d_name);
  317.                                 q += lname;
  318.                                 *q++ = '/';
  319.                                 strcpy(q,etc);    /* what if p is null string? */
  320.                                 ff_scan_file(ff,temp);
  321.                                 ifree(temp);
  322.                                 ifree(etc);
  323.                             }
  324.                             ifree(temp2);
  325.                         }
  326.                     }
  327.                     else    /* it isn't a directory */
  328.                     {
  329.                         if(!strchr(foll,'/'))
  330.                         {
  331.                             if(match_wild(foll,d->d_name))
  332.                             {
  333.                                 temp = (Char *)imalloc(lpred + 1 + lname + 1);
  334.                                 strcpy((p = temp),pred);
  335.                                 p += lpred;
  336.                                 *p++ = '/';
  337.                                 strcpy(p,d->d_name);
  338.                                 ff_report_name(ff,temp);
  339.                                 ifree(temp);
  340.                             }
  341.                         }
  342.                     }
  343.                 }    /* end of stat() block */
  344.                 ifree(full);
  345.             }
  346.             closedir(dp);
  347.         }    /* end of opendir() block */
  348.         ifree(foll);
  349.     }
  350.     else    /* wildcard is not /.../ */
  351.     {
  352.         foll = got_string((*p)? p : (Char *)"*");    /* p points past preceeding / */
  353.         if((p = (Char *)strchr(foll,'/')))
  354.         {
  355.             *p++ = '\0';
  356.             dirmatch = got_string(foll);
  357.             etc = got_string((*p)? p : (Char *)"*");
  358.             letc = strlen(etc);
  359.             if((dp = opendir(pred)))
  360.             {
  361.                 lpred = strlen(pred);
  362.                 while((d = readdir(dp)))    /* read entries */
  363.                 {
  364. #ifndef GNUDOS
  365.                     if(!d->d_ino)    /* skip empty entries */
  366.                         continue;
  367. #endif
  368.                     if(!strcmp(d->d_name,".") || !strcmp(d->d_name,".."))    /* ignore . and .. */
  369.                         continue;
  370. /* build pred/name */
  371.                     full = (Char *)imalloc(lpred + 1 + (lname = strlen(d->d_name)) + 1);
  372.                     memcpy(full,pred,lpred);
  373.                     p = full + lpred;
  374.                     *p++ = '/';
  375.                     memcpy(p,d->d_name,lname);
  376.                     p += lname;
  377.                     *p = '\0';
  378.                     if(!stat(full,&statbuf))    /* see if the file found is a directory */
  379.                     {
  380.                         if(S_ISDIR(statbuf.st_mode))
  381.                         {
  382.                             if(match_wild(dirmatch,d->d_name))
  383.                             {
  384.                                 temp = (Char *)imalloc(lpred + 1 + lname + 1 + letc + 1);
  385.                                 strcpy((q = temp),pred);
  386.                                 q += lpred;
  387.                                 *q++ = '/';
  388.                                 strcpy(q,d->d_name);
  389.                                 q += lname;
  390.                                 *q++ = '/';
  391.                                 strcpy(q,etc);
  392.                                 ff_scan_file(ff,temp);
  393.                                 ifree(temp);
  394.                             }
  395.                         }    /* if it isn't a directory, we are not interested */
  396.                     }
  397.                     ifree(full);
  398.                 }
  399.                 closedir(dp);
  400.             }
  401.             ifree(etc);
  402.             ifree(dirmatch);
  403.         }
  404.         else    /* foll contains no / */
  405.         {
  406.             if((dp = opendir(pred)))
  407.             {
  408.                 lpred = strlen(pred);
  409.                 while((d = readdir(dp)))    /* read entries */
  410.                 {
  411. #ifndef GNUDOS
  412.                     if(!d->d_ino)    /* skip empty entries */
  413.                         continue;
  414. #endif
  415.                     if(!strcmp(d->d_name,".")) /* || !strcmp(d->d_name,"..")) */
  416.                         continue;          /* Add this and .. not in dired */
  417. /* build pred/name */
  418.                     if(match_wild(foll,d->d_name))
  419.                     {
  420.                         full = (Char *)imalloc(lpred + 1 + (lname = strlen(d->d_name)) + 1);
  421.                         memcpy(full,pred,lpred);
  422.                         p = full + lpred;
  423.                         *p++ = '/';
  424.                         memcpy(p,d->d_name,lname);
  425.                         p += lname;
  426.                         *p = '\0';
  427.                         ff_report_name(ff,full);
  428.                         ifree(full);
  429.                     }
  430.                 }
  431.                 closedir(dp);
  432.             }
  433.         }    /* end of foll-contains-/ ifelse block */
  434.         ifree(foll);
  435.     }
  436.     ifree(wil);
  437. }
  438. #endif
  439.     
  440. /******************************************************************************\
  441. |Routine: find_files
  442. |Callby: cfg file_list_i load_file
  443. |Purpose: To initialize a wildcard search. Returns a nonzero context value
  444. |         if successful, else zero.
  445. |Arguments:
  446. |    wild is the wildcard filename string.
  447. \******************************************************************************/
  448. Long find_files(wild)
  449. Char  *wild;
  450. {
  451. #ifdef VMS
  452.     desc_node a,b;
  453.     Int status;
  454.     Long context;
  455.     Char *p,c;
  456.     Char ret[512];
  457.  
  458.     strcpy(wildcard,wild);
  459.     a.length = strlen(wildcard);
  460.     a.class = 0x010e;
  461.     a.string = wildcard;
  462.     b.length = 128;
  463.     b.class = 0x010e;
  464.     b.string = ret;
  465.     context = 0;
  466.     if((status = lib$find_file(&a,&b,&context)) & 1)
  467.     {
  468.         if((p = strchr(ret,' ')))
  469.             *p = '\0';
  470.         strcpy(lastmatch,ret);
  471.     }
  472.     else
  473.     {
  474.         lastmatch[0] = '\0';
  475.         context = 0;
  476.     }
  477.     return(context);
  478. #else
  479.     ff_ptr ff;
  480.     Char *wil,*q;
  481.     Char **tags;
  482.     namelist_ptr p;
  483.     Int n;
  484. #ifdef GNUDOS
  485.     Char c,*newwil,*w;
  486. #endif
  487.  
  488.     wil = got_string(wild);
  489. #ifdef GNUDOS
  490. /*    Check for 2nd char ':' (if so, device letter 1st char, directory after)
  491.  *    if directory spec starts with /, add /. to front
  492.  *    else if directory spec starts with non ../ or ./ add ./
  493.  */
  494.     for(q = wil;(c = *q);q++)
  495.         if(c == '\\')    /* convert \ to / */
  496.             *q = '/';
  497.     w = wil;
  498.     newwil = (Char *)imalloc(strlen(wil) + 3);    /* make buffer for fixed-up file name */
  499.     q = newwil;
  500.     if(*(w + 1) == ':')    /* copy the device spec if present */
  501.     {
  502.         *q++ = *w++;
  503.         *q++ = *w++;
  504.     }
  505.     *q = '\0';    /* terminate the device spec */
  506.     if(*w == '/')        /* if the first char is /, prepend /. */
  507.         strcat(q,"/.");
  508.     else if(!(            /* if the first char is not ./ or ../, prepend ./ */
  509.         (*w == '.' && *(w + 1) == '/') || 
  510.         (*w == '.' && *(w + 1) == '.' && *(w + 2) == '/')
  511.         ))
  512.         strcat(q,"./");
  513.     strcat(q,w);
  514.     ifree(wil);
  515.     wil = newwil;
  516. #else
  517.     wil = got_string(wild);
  518.     if(!(
  519.         *wil == '/' || 
  520.         (*wil == '.' && *(wil + 1) == '/') || 
  521.         (*wil == '.' && *(wil + 1) == '.' && *(wil + 2) == '/')
  522.         ))    /* if the first char is not /, prepend ./ */
  523.     {
  524.         q = (Char *)imalloc(strlen(wil) + 3);
  525.         strcpy(q,"./");
  526.         strcat(q,wil);
  527.         ifree(wil);
  528.         wil = q;
  529.     }
  530. #endif
  531.     ff = (ff_ptr)imalloc(sizeof(ff_node));
  532.     ff->magic = CHARLES;
  533.     ff->matchcount = 0;
  534.     ff->firstname = NULL;
  535.     ff_scan_file(ff,wil);
  536.     ifree(wil);
  537. /* sort namelist */
  538.     if(ff->matchcount)
  539.     {
  540.         tags = (Char **)imalloc((ff->matchcount + 1) * sizeof(Char *));
  541.         for(n = 0,p = ff->firstname;p;p = p->next)
  542.             tags[n++] = p->name;
  543.         cqsort_p(n,tags);
  544.         for(n = 0,p = ff->firstname;p;p = p->next)
  545.             p->name = tags[n++];
  546.         ifree(tags);
  547.     }
  548.     ff->curname = ff->firstname;
  549.     return((Long)ff);
  550. #endif
  551. }
  552.  
  553. /******************************************************************************\
  554. |Routine: next_file
  555. |Callby: cfg file_list_i load_file
  556. |Purpose: Returns the next matching file name. Returns 1 on success, else zero.
  557. |Arguments:
  558. |    context is a context value previously returned by find_files.
  559. |    retbuf gets the returned file name.
  560. \******************************************************************************/
  561. Int next_file(context,retbuf)
  562. Long context;
  563. Char *retbuf;
  564. {
  565. #ifdef VMS
  566.     desc_node a,b;
  567.     Char *p,c;
  568.  
  569.     if(lastmatch[0] == '\0')
  570.         return(0);
  571.     strcpy(retbuf,lastmatch);
  572.     a.length = strlen(wildcard);
  573.     a.class = 0x010e;
  574.     a.string = wildcard;
  575.     b.length = 128;
  576.     b.class = 0x010e;
  577.     b.string = lastmatch;
  578.     if(lib$find_file(&a,&b,&context) & 1)
  579.     {
  580.         if((p = strchr(lastmatch,' ')))
  581.             *p = '\0';
  582.     }
  583.     else
  584.         lastmatch[0] = '\0';
  585.     return(1);
  586. #else
  587.     namelist_ptr p;
  588.     ff_ptr ff;
  589.     
  590.     ff = (ff_ptr)context;
  591.     if(ff->magic != CHARLES)
  592.     {
  593.         retbuf[0] = '\0';
  594.         return(0);
  595.     }
  596.     if(!(p = ff->curname))
  597.     {
  598.         ff->curname = ff->firstname;
  599.         retbuf[0] = '\0';
  600.         return(0);
  601.     }
  602.     ff->curname = p->next;
  603.     strcpy(retbuf,p->name);
  604.     return(1);
  605. #endif
  606. }
  607.  
  608. /******************************************************************************\
  609. |Routine: find_files_end
  610. |Callby: cfg file_list_i load_file
  611. |Purpose: To clean up after find_file() is called.
  612. |Arguments:
  613. |    context is the context value used by find_file().
  614. \******************************************************************************/
  615. void find_files_end(context)
  616. Long context;
  617. {
  618. #ifdef VMS
  619.     if(context)
  620.         lib$find_file_end(&context);
  621. #else
  622.     ff_ptr ff;
  623.     namelist_ptr p,q;
  624.     
  625.     if((ff = (ff_ptr)context))
  626.         if(ff->magic == CHARLES)
  627.         {
  628.             for(p = ff->firstname;p;)
  629.             {
  630.                 q = p -> next;
  631.                 ifree(p->name);
  632.                 ifree(p);
  633.                 p = q;
  634.             }
  635.             ifree(ff);
  636.         }
  637. #endif
  638. }
  639.  
  640.